#include "hw_config.h"
#include <aos/aos.h>
#include <yoc/eventid.h>
#include <ctype.h>
#include <string.h>
#include "app_main.h"

#include <drv/gpio.h>
#include <pinmux.h>
#include <pin_name.h>
#include <voice.h>

#include <devices/devicelist.h>
#include <devices/wifi.h>
#include <devices/flash.h>
#include <devices/uart.h>
#include <yoc/record.h>
#include <yoc/mic.h>
#include <media.h>

#include "fct.h"

#define TAG "fct_case"

static int test_fctstate(int isquery, int arg_num, char **args, char *test_result)
{
    if (isquery) {
        char fct_state[64] = {0};
        
        aos_kv_getstring("fct_test_state", fct_state, 64);

        strcpy(test_result, fct_state);
    } else if (arg_num == 1 && strlen(args[0]) <= 64) {
        char *fct_state = args[0];
        int ret;

        ret = aos_kv_setstring("fct_test_state", fct_state);
        if (ret != 0) {
            return FTEST_RESULT_ERR_FAILED;
        }
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int test_passed(int isquery, int arg_num, char **args, char *test_result)
{
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
        int ret;

        ret = aos_kv_del("factory_test_mode");
        if (ret != 0) {
            return FTEST_RESULT_ERR_FAILED;
        }
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int gpio_test(int isquery, int arg_num, char **args, char *test_result)
{
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
        
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

// static int ram_test(int isquery, int arg_num, char **args, char *test_result)
// {
//     #define RAM_TEST_LEN    64

//     int *sramsha_test_addr = (int *)0x01003000;
//     int *sramsys_test_addr = (int *)0x20007000;
//     char wtbytes[RAM_TEST_LEN], cpbytes[RAM_TEST_LEN];

//     memset((char *)wtbytes, 0x11, RAM_TEST_LEN);

//     if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
//         memcpy(cpbytes, sramsha_test_addr, RAM_TEST_LEN);
//         memcpy(sramsha_test_addr, wtbytes, RAM_TEST_LEN);
//         if (memcmp(sramsha_test_addr, wtbytes, RAM_TEST_LEN) != 0) {
//             return FTEST_RESULT_ERR_FAILED;
//         }
//         memcpy(sramsha_test_addr, cpbytes, RAM_TEST_LEN);

//         memcpy(cpbytes, sramsys_test_addr, RAM_TEST_LEN);
//         memcpy(sramsys_test_addr, wtbytes, RAM_TEST_LEN);
//         if (memcmp(sramsys_test_addr, wtbytes, RAM_TEST_LEN) != 0) {
//             return FTEST_RESULT_ERR_FAILED;
//         }
//         memcpy(sramsys_test_addr, cpbytes, RAM_TEST_LEN);
//     } else {
//         return FTEST_RESULT_ERR_PARAM;
//     }

//     return FTEST_RESULT_SUCCESS;
// }

static int flash_test(int isquery, int arg_num, char **args, char *test_result)
{
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
#define TEST_LEN    (2000)

        aos_dev_t *flash_dev = NULL;
        uint32_t flash_test_addr = 0x8687000 - 0x8000000;
        char wtbytes[TEST_LEN], rdbytes[TEST_LEN];
        int ret;

        flash_dev = flash_open("eflash0");
        CHECK_RET_TAG_WITH_RET(flash_dev != NULL, FTEST_RESULT_ERR_FAILED);

        ret = flash_erase(flash_dev, flash_test_addr, 1);
        CHECK_RET_TAG_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        memset((char *)wtbytes, 0xA5, TEST_LEN);
        ret = flash_program(flash_dev, flash_test_addr, wtbytes, TEST_LEN);
        CHECK_RET_TAG_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        ret = flash_read(flash_dev, flash_test_addr, rdbytes, TEST_LEN);
        CHECK_RET_TAG_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        ret = memcmp(wtbytes, rdbytes, TEST_LEN);
        CHECK_RET_TAG_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        ret = flash_erase(flash_dev, flash_test_addr, 1);
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int key_test(int isquery, int arg_num, char **args, char *test_result)
{
    #define KEY_READ_TIME   10

#if defined(APP_KEY_MUTE) && defined(APP_KEY_VOL_INC) && defined(APP_KEY_VOL_DEC) 
    if (!isquery && arg_num == 1) {
        int key_id = atoi(args[0]);
        int adc_pin[3] = {APP_KEY_MUTE, APP_KEY_VOL_INC, APP_KEY_VOL_DEC};
        bool key_state;
        int adc_key_num = sizeof(adc_pin) / sizeof(int);
        gpio_pin_handle_t     hdl_key;

        if (key_id <= 0 || key_id > adc_key_num) {
            return FTEST_RESULT_ERR_PARAM;   
        }

        drv_pinmux_config(adc_pin[key_id-1], PIN_FUNC_GPIO);
        hdl_key = csi_gpio_pin_initialize(adc_pin[key_id-1], NULL);
        csi_gpio_pin_config_mode(hdl_key, GPIO_MODE_PULLNONE);
        csi_gpio_pin_config_direction(hdl_key, GPIO_DIRECTION_INPUT);
        csi_gpio_pin_read(hdl_key, &key_state);
        csi_gpio_pin_uninitialize(hdl_key);        

        char result[2] = {0};
        result[0] = '0' + (key_state ? 0 : 1);
        strcpy(test_result, result);
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }
#endif
    return FTEST_RESULT_SUCCESS;
}

static int led_test(int isquery, int arg_num, char **args, char *test_result)
{
#if defined(LED0_PIN) && defined(LED1_PIN)
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
        gpio_pin_handle_t     hdl_led0, hdl_led1;
        int pin_level = 1;

        drv_pinmux_config(LED0_PIN, PIN_FUNC_GPIO);
        hdl_led0 = csi_gpio_pin_initialize(LED0_PIN, NULL);
        csi_gpio_pin_config_mode(hdl_led0, GPIO_MODE_PUSH_PULL);
        csi_gpio_pin_config_direction(hdl_led0, GPIO_DIRECTION_OUTPUT);

        drv_pinmux_config(LED1_PIN, PIN_FUNC_GPIO);
        hdl_led1 = csi_gpio_pin_initialize(LED1_PIN, NULL);
        csi_gpio_pin_config_mode(hdl_led1, GPIO_MODE_PUSH_PULL);
        csi_gpio_pin_config_direction(hdl_led1, GPIO_DIRECTION_OUTPUT);

        for (int i = 0; i < 20; i++) {
            csi_gpio_pin_write(hdl_led0, pin_level);
            csi_gpio_pin_write(hdl_led1, pin_level);
            pin_level = !pin_level;

            aos_msleep(100);
        }
        
        csi_gpio_pin_uninitialize(hdl_led0);
        csi_gpio_pin_uninitialize(hdl_led1);
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }
#endif
    return FTEST_RESULT_SUCCESS;
}

static int mac_test(int isquery, int arg_num, char **args, char *test_result)
{
    if (isquery) {
        char mac_str[18] = {0};
        
        extern int wifi_get_mac_address(char *);
        int ret = wifi_get_mac_address(mac_str);
        CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        strcpy(test_result, mac_str);
    } else if (arg_num == 1 && strlen(args[0]) == 17) {   // xx:xx:xx:xx:xx:xx
        char *mac = args[0];
        int n = 0;
        for (int i = 0; i < 17; ++i) {
            if (mac[i] == ':') {
                continue;
            }

            mac[n++] = mac[i];
        }
        mac[n] = 0;

        if (strlen(mac) != 12) {
            return FTEST_RESULT_ERR_PARAM;
        }

        extern int wifi_set_mac_address(char * mac);
        wifi_set_mac_address(mac);
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int alidevinfo_test(int isquery, int arg_num, char **args, char *test_result)
{
    #define ALI_INFO_MAX_LEN 64

    if (isquery) {
        char product_key[ALI_INFO_MAX_LEN] = {0};
        char product_secret[ALI_INFO_MAX_LEN] = {0};
        char device_name[ALI_INFO_MAX_LEN] = {0};
        char device_secret[ALI_INFO_MAX_LEN] = {0};
        char device_info[ALI_INFO_MAX_LEN * 4];
        
        aos_kv_getstring("hal_devinfo_pk", product_key, ALI_INFO_MAX_LEN);
        aos_kv_getstring("hal_devinfo_ps", product_secret, ALI_INFO_MAX_LEN);
        aos_kv_getstring("hal_devinfo_dn", device_name, ALI_INFO_MAX_LEN);
        aos_kv_getstring("hal_devinfo_ds", device_secret, ALI_INFO_MAX_LEN);

        snprintf(device_info, ALI_INFO_MAX_LEN * 4,"%s,%s,%s,%s", product_key, product_secret, device_name, device_secret);
        strcpy(test_result, device_info);
    } else if (arg_num == 4) {
        int ret;
        char *pk = args[0];
        char *ps = args[1];
        char *dn = args[2];
        char *ds = args[3];

        if (strlen(pk) > 0) {
            ret = aos_kv_setstring("hal_devinfo_pk", pk);
            CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);
        }

        if (strlen(ps) > 0) {
            ret = aos_kv_setstring("hal_devinfo_ps", ps);
            CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);
        }

        if (strlen(dn) > 0) {
            ret = aos_kv_setstring("hal_devinfo_dn", dn);
            CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);
        }

        if (strlen(ds) > 0) {
            ret = aos_kv_setstring("hal_devinfo_ds", ds);
            CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);
        }
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int rtc_test(int isquery, int arg_num, char **args, char *test_result)
{
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
        time_t time_before = rtc_to_system();
        aos_msleep(1020);
        time_t time_now = rtc_to_system();

        if (time_now <= time_before) {
            return FTEST_RESULT_ERR_FAILED;
        }
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

#define CHUNK_SIZE (5 * 3200)

#define EVT_MEDIA_FINISHED      (1 << 0)
#define EVT_MEDIA_ERROR         (1 << 1)

static aos_event_t evt_media_stat;

extern void play_sin(int second);

void tsk_play_sin(void *arg)
{
    int sec = (int)arg;

    play_sin(sec);
}

#define WS_URL_TEMPLATE "ws://%s:%s"
static int control_source_pcm(int start, const char *ip, const char *port)
{
    voice_t * v = (voice_t *)mic_get_privdata();
    char url[128];
    int ret;

    if (start) {
        LOGD(TAG, "mic rec start.");

        voice_backflow_control(v, VOICE_VAD_DATA, 0);
        voice_backflow_control(v, VOICE_KWS_DATA, 0);
        snprintf(url, 128, WS_URL_TEMPLATE, ip, port);
        ret = aui_mic_rec_start(url, "mic.pcm");
        CHECK_RET_WITH_RET(ret == 0, -1);
    } else {
        aui_mic_rec_stop();
    }

    return 0;
}


static void media_state_cb(uint32_t event_id, const void *param, void *context)
{
    switch (event_id) {
        case EVENT_MEDIA_SYSTEM_ERROR:
            aos_event_set(&evt_media_stat, EVT_MEDIA_ERROR, AOS_EVENT_OR);
            break;

        case EVENT_MEDIA_SYSTEM_FINISH:
            aos_event_set(&evt_media_stat, EVT_MEDIA_FINISHED, AOS_EVENT_OR);
            break;
        default:;
    }
}

/**
 * AT+VOICE=4,192.168.1.104,8090
 */
static int voice_test(int isquery, int arg_num, char **args, char *test_result)
{
    int ret;

    if (!isquery && arg_num == 3) {
        int mode = atoi(args[0]);
        char *ip = args[1];
        char *port = args[2];
        aos_task_t sin_tsk;

        if (mode < 1 || mode > 4) {
            return FTEST_RESULT_ERR_PARAM;
        }

        for (int i = 0; i < 50 && !wifi_internet_is_connected(); ++i) {
            aos_msleep(100);
        }

        if (!wifi_internet_is_connected()) {
            strcpy(test_result, "network err");
            return FTEST_RESULT_ERR_FAILED;
        }

        LOGD(TAG, "send to %s:%s", ip, port);
        aos_event_set(&evt_media_stat, 0, AOS_EVENT_AND);
        mplayer_stop(SOURCE_LOCAL, MEDIA_ALL);
        aos_task_new_ext(&sin_tsk, "sin1k", tsk_play_sin, (void *)10, 4096, AOS_DEFAULT_APP_PRI);

        ret = control_source_pcm(1, ip, port);
        CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);

        unsigned int flags;
        aos_event_get(&evt_media_stat, EVT_MEDIA_FINISHED | EVT_MEDIA_ERROR, AOS_EVENT_OR_CLEAR, &flags, AOS_WAIT_FOREVER);
        if (flags & EVT_MEDIA_ERROR) {
            return FTEST_RESULT_ERR_FAILED;
        }

        LOGD(TAG, "finish play sin");
        aos_msleep(200);
        ret = control_source_pcm(0, NULL, NULL);
        CHECK_RET_WITH_RET(ret == 0, FTEST_RESULT_ERR_FAILED);        
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int device_reboot(int isquery, int arg_num, char **args, char *test_result)
{
    if (!isquery && arg_num == 1 && args[0][0] == '0' && args[0][1] == 0) {
        aos_reboot();
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

static int read_softver(int isquery, int arg_num, char **args, char *test_result)
{
    if (isquery) {
        char *softver = aos_get_os_version();
        if (softver) {
            strcpy(test_result, softver);
            test_result[strlen(softver)] = 0;
        }

        return FTEST_RESULT_SUCCESS;
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

#define DEVICE_HW_VERSION  "MIT_DVT"
static int read_hwver(int isquery, int arg_num, char **args, char *test_result)
{
    if (isquery) {
        strcpy(test_result, DEVICE_HW_VERSION);
        test_result[strlen(DEVICE_HW_VERSION)] = 0;

        return FTEST_RESULT_SUCCESS;
    } else {
        return FTEST_RESULT_ERR_PARAM;
    }

    return FTEST_RESULT_SUCCESS;
}

void fct_case_init(void)
{
    utask_t *task = NULL;
    int wifi_test = 0;
    aos_dev_t *comm_handle = uart_open_id("uart", CONSOLE_IDX);
    CHECK_RET_WITH_RET(comm_handle != NULL, );

    aos_kv_getint("factory_wifi_test", &wifi_test);
    if (wifi_test) {
        // extern void hal_wifi_test_enabled(int en);
        // hal_wifi_test_enabled(1);
        task = utask_new("at&cli", 6 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
        CHECK_RET_WITH_GOTO(task != NULL, ERR);

        cli_service_init(task);

        // cli_reg_cmd_ifconfig_wifi();
        cli_reg_cmd_sysinfo();
        cli_reg_cmd_kvtool();

        while(1) aos_msleep(10000);
    } 
    // else {
    //     // aos_set_log_level(AOS_LL_ERROR);
    //     console_init(2, 115200, 512);   // put 803 print to uart3
    // }

    for (int i = 0; i < 5; i++) {
        uart_send(comm_handle, "bonjour SC5654\r\n", strlen("bonjour SC5654\r\n"));
        aos_msleep(300);
    }

    aos_event_new(&evt_media_stat, 0);
    event_subscribe(EVENT_MEDIA_SYSTEM_FINISH, media_state_cb, NULL);
    event_subscribe(EVENT_MEDIA_SYSTEM_ERROR, media_state_cb, NULL);

    task = utask_new("fct", 8 * 1024, 150, AOS_DEFAULT_APP_PRI + 5);
    CHECK_RET_WITH_GOTO(task, ERR);

    fct_init(1, 115200, task);

    fct_register_cmd("FCTSTAT",     NULL, test_fctstate);
    fct_register_cmd("FCTPASSED",   NULL, test_passed);
    fct_register_cmd("GPIO",        NULL, gpio_test);
    fct_register_cmd("MAC",         NULL, mac_test);
    fct_register_cmd("ALIDEVINFO",  NULL, alidevinfo_test);
    // fct_register_cmd("RAM",         NULL, ram_test);
    fct_register_cmd("RTC",         NULL, rtc_test);
    fct_register_cmd("FLASH",       NULL, flash_test);
    fct_register_cmd("KEY",         NULL, key_test);
    fct_register_cmd("LED",         NULL, led_test);
    fct_register_cmd("VOICE",       NULL, voice_test);
    fct_register_cmd("REBOOT",      NULL, device_reboot);
    fct_register_cmd("SWVER",       NULL, read_softver);
    fct_register_cmd("HWVER",       NULL, read_hwver);

    while (1) aos_msleep(10000);
    
    return;

ERR:
    LOGD(TAG, "fct init failed");
}
